package com.yida.gwj.config.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.yida.common.core.exception.ServiceException;
import com.yida.common.core.utils.DateUtils;
import com.yida.common.core.utils.file.FileTypeUtils;
import com.yida.common.core.utils.file.MimeTypeUtils;
import com.yida.common.security.utils.SecurityUtils;
import com.yida.gwj.common.constant.GwjConstants;
import com.yida.gwj.config.domain.GwjAttachmentConfig;
import com.yida.gwj.config.domain.GwjAttachmentInfo;
import com.yida.gwj.config.domain.GwjAttachmentStatus;
import com.yida.gwj.config.domain.request.GwjAttachementUploadRequest;
import com.yida.gwj.config.domain.request.GwjAttachmentInfoRequest;
import com.yida.gwj.config.enums.BusinessAttachmentTypeEnum;
import com.yida.gwj.config.mapper.GwjAttachmentConfigMapper;
import com.yida.gwj.config.mapper.GwjAttachmentInfoMapper;
import com.yida.gwj.config.mapper.GwjAttachmentStatusMapper;
import com.yida.gwj.config.service.IGwjAttachmentInfoService;
import com.yida.gwj.config.util.RedisConstants;
import com.yida.system.api.RemoteFileService;
import com.yida.system.api.domain.UploadResultDTO;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;


/**
 * 附件信息Service业务层处理
 *
 * @author ruoyi
 * @date 2023-04-19
 */
@Service
public class GwjAttachmentInfoServiceImpl implements IGwjAttachmentInfoService {
    @Resource
    private GwjAttachmentInfoMapper infoMapper;

    @Resource
    private GwjAttachmentConfigMapper configMapper;

    @Resource
    private GwjAttachmentStatusMapper statusMapper;

    @Resource
    private RemoteFileService remoteFileService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 查询附件信息
     *
     * @param id 附件信息主键
     * @return 附件信息
     */
    @Override
    public GwjAttachmentInfo selectGwjAttachmentInfoById(Long id) {
        return infoMapper.selectGwjAttachmentInfoById(id);
    }

    /**
     * 查询附件信息列表
     *
     * @param gwjAttachmentInfo 附件信息
     * @return 附件信息
     */
    @Override
    public List<GwjAttachmentInfo> selectGwjAttachmentInfoList(GwjAttachmentInfo gwjAttachmentInfo) {
        return infoMapper.selectGwjAttachmentInfoList(gwjAttachmentInfo);
    }

    /**
     * 新增附件信息
     *
     * @param request 附件信息
     * @return 结果
     */
    @Override
    public int insertGwjAttachmentInfo(GwjAttachmentInfoRequest request) {
        if (ObjUtil.isEmpty(configMapper.selectGwjAttachmentConfigById(Long.valueOf(request.getOutServerIdentification())))) {
            throw new ServiceException("请输入正确的外部业务标识");
        }
        GwjAttachmentInfo bean = BeanUtil.copyProperties(request, GwjAttachmentInfo.class);
        bean.setOperId(SecurityUtils.getUserId());
        bean.setOperName(SecurityUtils.getUsername());
        bean.setCreateTime(DateUtils.getNowDate());
        bean.setCreateBy(SecurityUtils.getUsername());
        return infoMapper.insertGwjAttachmentInfo(bean);
    }

    /**
     * 修改附件信息
     *
     * @param gwjAttachmentInfo 附件信息
     * @return 结果
     */
    @Override
    public int updateGwjAttachmentInfo(GwjAttachmentInfo gwjAttachmentInfo) {
        gwjAttachmentInfo.setUpdateTime(DateUtils.getNowDate());
        return infoMapper.updateGwjAttachmentInfo(gwjAttachmentInfo);
    }

    /**
     * 批量删除附件信息
     *
     * @param ids 需要删除的附件信息主键
     * @return 结果
     */
    @Override
    public int deleteGwjAttachmentInfoByIds(String[] ids) {
        return infoMapper.deleteGwjAttachmentInfoByIds(ids);
    }

    /**
     * 删除附件信息信息
     *
     * @param id 附件信息主键
     * @return 结果
     */
    @Override
    public int deleteGwjAttachmentInfoById(String id) {
        return infoMapper.deleteGwjAttachmentInfoById(id);
    }

    @Override
    public int receiptAttachment(List<Long> ids) {
        return infoMapper.updateBatch(ids, Integer.valueOf(GwjConstants.YES));
    }

    @Override
    public int cancelReceipt(List<Long> ids) {
        return infoMapper.updateBatch(ids, Integer.valueOf(GwjConstants.NO));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int upload(GwjAttachementUploadRequest request, MultipartFile[] files) {
        //附件状态校验设置
        checkStatus(request);
        //附件配置校验
        GwjAttachmentConfig config = checkAttachConfig(request);
        //附件后缀校验
        switch (BusinessAttachmentTypeEnum.getByCode(request.getType())) {
            case PICTURE:
                checkExtension(files, MimeTypeUtils.IMG_ALLOWED_EXTENSION);
                break;
            case FILE:
                checkExtension(files, MimeTypeUtils.FILE_ALLOWED_EXTENSION);
                break;
            default:
                break;
        }
        //附件上传
        ArrayList<UploadResultDTO> uploadResults;
        try {
            uploadResults = remoteFileService.upload(request.getBucketName(), request.getFileNames(), files).getData();
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        //批量记录附件信息
        int result = 0;
        List<GwjAttachmentInfo> infoList = Collections.emptyList();
        if (CollUtil.isNotEmpty(uploadResults)) {
            infoList = uploadResults.stream().map(item -> {
                GwjAttachmentInfo info = new GwjAttachmentInfo() {{
                    setContent(JSONUtil.toJsonStr(item));
                    setServerIdentification(request.getServerIdentification().toString());
                    setOutServerIdentification(config.getServerIdentification());
                    setOperId(SecurityUtils.getUserId());
                    setOperName(SecurityUtils.getUsername());
                    setCreateBy(SecurityUtils.getUsername());
                }};
                return info;
            }).collect(Collectors.toList());
            result = infoMapper.insertBatch(infoList);
        }
        //记录附件状态并缓存
        String key = RedisConstants.ATTACH_STATUS_KEY + request.getOutServerIdentification() + request.getServerIdentification();
        statusMapper.insert(new GwjAttachmentStatus() {{
            setServerIdentification(request.getServerIdentification().toString());
            setOutServerIdentification(config.getServerIdentification());
            setCreateBy(SecurityUtils.getUsername());
        }});
        if (!redisTemplate.hasKey(key)) {
            redisTemplate.opsForValue().set(key, GwjConstants.YES, 2, TimeUnit.HOURS);
        }
        return result;
    }


    /**
     * 附件状态校验
     *
     * @param request
     */
    private void checkStatus(GwjAttachementUploadRequest request) {
        String key = RedisConstants.ATTACH_STATUS_KEY + request.getOutServerIdentification() + request.getServerIdentification();
        Integer allowUpFlag = 1;
        if (redisTemplate.hasKey(key)) {
            allowUpFlag = Integer.valueOf(redisTemplate.opsForValue().get(key));
        } else {
            GwjAttachmentStatus status = statusMapper.select(request.getServerIdentification().toString(), request.getOutServerIdentification());
            if (ObjUtil.isNotEmpty(status)) {
                allowUpFlag = status.getAllowUpFlag();
                redisTemplate.opsForValue().set(key, allowUpFlag.toString(), 2, TimeUnit.HOURS);
            }
        }
        if (allowUpFlag.compareTo(Integer.valueOf(GwjConstants.YES)) != 0) {
            throw new ServiceException("附件不允许上传");
        }
    }

    /**
     * 附件配置校验
     *
     * @param request
     * @return
     */
    private GwjAttachmentConfig checkAttachConfig(GwjAttachementUploadRequest request) {
        GwjAttachmentConfig config;
        String key = RedisConstants.ATTACH_CONFIG_KEY + request.getAreaId() + request.getOutServerIdentification() + request.getServerType() + request.getType();
        if (redisTemplate.hasKey(key)) {
            config = JSONUtil.toBean(redisTemplate.opsForValue().get(key), GwjAttachmentConfig.class);
        } else {
            config = Opt.ofNullable(configMapper.select(new GwjAttachmentConfig() {{
                setAreaId(request.getAreaId());
                setOutServerIdentification(request.getOutServerIdentification());
                setServerType(request.getServerType());
                setType(request.getType());
            }})).ifPresentOrElse(item -> {
                //过期时间2小时
                redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(item), 2, TimeUnit.HOURS);
            }, () -> {
                throw new ServiceException("附件类型不合法！");
            }).get();
        }
        return config;
    }

    /**
     * 文件扩展名校验
     *
     * @param files            文件集合
     * @param allowedExtension 允许后缀集合
     */
    private void checkExtension(MultipartFile[] files, String[] allowedExtension) {
        Arrays.asList(files).stream().map(file -> FileTypeUtils.getExtension(file)).forEach(extension -> {
            if (!Arrays.asList(allowedExtension).contains(extension)) {
                throw new ServiceException("文件格式异常，请检查后重试！");
            }
        });
    }

}
